
/*
 * Copyright (C) Igor Sysoev
 * Copyright (C) Nginx, Inc.
 */


#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include "redis_model.h"
#include "config_factory.h"
#include "ngx_http_huidu_module_handle.h"


#define   CONFIG_INTERVAL  10  //

#define REJECT_TYPE 1
#define MAX_REDIRECT_URI_LEN 255

static void * ngx_http_huidu_create_conf(ngx_conf_t *cf);
static char * ngx_http_huidu_merge_conf(ngx_conf_t *cf, void *parent, void *child);
static char * ngx_http_huidu_redis_address(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_int_t ngx_http_huidu_init(ngx_conf_t *cf);
static char * ngx_http_set_huidu_header_token(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static char * ngx_http_set_model_switch(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
//static ngx_int_t init_master_function(ngx_log_t *log);
static ngx_int_t init_worker_function(ngx_cycle_t *cycle);


static ngx_conf_enum_t  ngx_http_huidu_log_levels[] = {
    { ngx_string("info"), NGX_LOG_INFO },
    { ngx_string("notice"), NGX_LOG_NOTICE },
    { ngx_string("warn"), NGX_LOG_WARN },
    { ngx_string("error"), NGX_LOG_ERR },
    { ngx_null_string, 0 }
};

static ngx_conf_num_bounds_t  ngx_http_huidu_status_bounds = {
    ngx_conf_check_num_bounds, 400, 599
};


static ngx_command_t  ngx_http_huidu_module_commands[] = {

    { ngx_string("huidu_redis_addresses"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
      ngx_http_huidu_redis_address,
      NGX_HTTP_LOC_CONF_OFFSET,
      0,
      NULL },
	 { ngx_string("huidu_header_token"),
	   NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
	   ngx_http_set_huidu_header_token,
	   NGX_HTTP_LOC_CONF_OFFSET,
	   0,
	   NULL },
	 { ngx_string("huidu_model_switch"),
	   NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
	   ngx_http_set_model_switch,
	   NGX_HTTP_LOC_CONF_OFFSET,
	   offsetof(ngx_http_huidu_conf_t, model_switch),
	   NULL },
    { ngx_string("huidu_log_level"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
      ngx_conf_set_enum_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_huidu_conf_t, log_level),
      &ngx_http_huidu_log_levels },

    { ngx_string("huidu_status"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
      ngx_conf_set_num_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_huidu_conf_t, status_code),
      &ngx_http_huidu_status_bounds },

      ngx_null_command
};


static ngx_http_module_t  ngx_http_huidu_module_ctx = {
    NULL,                                /* preconfiguration */
    ngx_http_huidu_init,              /* postconfiguration */

    NULL,                                /* create main configuration */
    NULL,                                /* init main configuration */

    NULL,                                /* create server configuration */
    NULL,                                /* merge server configuration */

    ngx_http_huidu_create_conf,       /* create location configuration */
    ngx_http_huidu_merge_conf         /* merge location configuration */
};


ngx_module_t  ngx_http_huidu_module = {
    NGX_MODULE_V1,
    &ngx_http_huidu_module_ctx,       /* module context */
    ngx_http_huidu_module_commands,          /* module directives */
    NGX_HTTP_MODULE,                       /* module type */
	NULL,                 					/* init master */
	NULL,                					/* init module */
	init_worker_function,                   /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};

static ngx_int_t
ngx_http_huidu_handler(ngx_http_request_t *r)
{

    ngx_http_huidu_conf_t    *scf;
  //  clock_t  start_time, end_time;
   // start_time = clock();

    ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,"ngx_http_huidu_handler begin \n");
    scf = ngx_http_get_module_loc_conf(r, ngx_http_huidu_module);
    if(NULL == scf){
    	ngx_log_error(NGX_LOG_ERR,r->connection->log, 0,"ERROR !ngx_http_huidu_handler ngx_http_huidu_conf_t = null");
    	return NGX_DECLINED;
    }

    ngx_log_error(NGX_LOG_INFO,r->connection->log, 0,"ngx_http_huidu_handler model_switch = %d\n", scf->model_switch);
    ngx_log_error(NGX_LOG_INFO,r->connection->log, 0,"header token name =%V\n",&(scf->header_key) );
    ngx_log_error(NGX_LOG_INFO,r->connection->log, 0,"header token value =%V\n",&(scf->header_value) );
    //判断总开关
    if(scf->model_switch == 0){
    	ngx_log_error(NGX_LOG_INFO,r->connection->log, 0,"WARNING !ngx_http_huidu_handler model_switch = 0");
    	return NGX_DECLINED;
    }


    printImInfo(r);

    huidu_request_handle(r, scf, g_huidu_config );

  //  end_time =clock();
  //  ngx_log_error(NGX_LOG_INFO,r->connection->log, 0,">>>>>>>>ngx_http_huidu_handler start_time = %ld, end_time=%ld, cost_time=%ld\n",start_time,end_time,(end_time-start_time));
  //  ngx_log_error(NGX_LOG_INFO,r->connection->log, 0,">>>>>>>>ngx_http_huidu_handler start_time = %d, end_time=%d, cost_time=%d\n",start_time,end_time,(end_time-start_time));
   // ngx_log_error(NGX_LOG_INFO,r->connection->log, 0,">>>>>>>>ngx_http_huidu_handler CLOCKS_PER_SEC = %d\n",CLOCKS_PER_SEC);
    return NGX_DECLINED;

  
}


static ngx_int_t
init_worker_function(ngx_cycle_t *cycle){

	 int err;
	 ngx_log_error(NGX_LOG_INFO,cycle->log, 0,"init_worker_function begin!");
	 err = start_config_thread(cycle->log);
	 ngx_log_error(NGX_LOG_INFO,cycle->log, 0,"init_worker_function ->start_config_thread, errcode = %d\n", err);

	 return  NGX_OK;
}


static void *
ngx_http_huidu_create_conf(ngx_conf_t *cf)
{
    ngx_http_huidu_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_huidu_conf_t));
    if (conf == NULL) {
       ngx_conf_log_error(NGX_LOG_ERR, cf, 0,"ngx_http_huidu_create_conf return NULL");
        return NULL;
    }

    /*
     * set by ngx_pcalloc():
     *
     *     conf->limits.elts = NULL;
     */


    conf->model_switch 				= NGX_CONF_UNSET_UINT;

    conf->log_level     = NGX_CONF_UNSET_UINT;
    conf->status_code 	= NGX_CONF_UNSET_UINT;
    conf->redis_count  	= 0;
   // conf->reject_type	= 0;

 //   ngx_conf_log_error(NGX_LOG_ERR, cf, 0,"ngx_http_huidu_create_conf  end, conf = %p", conf);
    return conf;
}


static char *
ngx_http_huidu_merge_conf(ngx_conf_t *cf, void *parent, void *child)
{
    ngx_http_huidu_conf_t *prev = parent;
    ngx_http_huidu_conf_t *conf = child;


    if (conf->redis_addr == NULL) {
       conf->redis_addr = prev->redis_addr;
       conf->redis_count = prev->redis_count;
    }

    /*
    if(conf->reject_type == 0){
    	conf->reject_type =  prev->reject_type;
    }
*/

	ngx_conf_merge_str_value(conf->header_key, prev->header_key, DEFAULT_HEADER_TOKEN_KEY);
    ngx_conf_merge_str_value(conf->header_value, prev->header_value, DEFAULT_HEADER_TOKEN_VALUE);
    ngx_conf_merge_uint_value(conf->model_switch, prev->model_switch, DEFAULT_MODEL_SWITCH);


    ngx_conf_merge_uint_value(conf->log_level, prev->log_level, NGX_LOG_ERR);
    ngx_conf_merge_uint_value(conf->status_code, prev->status_code,
                              NGX_HTTP_SERVICE_UNAVAILABLE);

//    ngx_conf_log_error(NGX_LOG_ERR, cf, 0,"ngx_http_huidu_merge_conf  end, parent = %p, child=%p",parent,  child);
    return NGX_CONF_OK;
}

static char * my_ngx_strchr(const char *s, char c, ngx_int_t max_len){
    char *pos = (char *)s;
    if(max_len <= 0) return NULL;
    while(pos - s <= max_len){
        if(*pos == c)
            return pos;
        pos ++;
    }

    return NULL;

}
static ngx_int_t get_redis_info(ngx_conf_t *cf, ngx_str_t *redis_info, ngx_http_huidu_redis_address_t * addr,  ngx_uint_t max_count){
    u_char      *p_start,*p_end, *p_port, *str_end;
    ngx_int_t   len, i;
    ngx_uint_t      port;       
    
    p_start = redis_info->data;
    str_end = redis_info->data + redis_info->len;
    for(i = 0; i < (ngx_int_t)max_count; i ++){
        
        p_port= (u_char *) my_ngx_strchr((const char *)p_start, ':', str_end-p_start);
        
        if(p_port == NULL)  return i; 
        p_end= (u_char *) my_ngx_strchr((const char *)p_port, ',', str_end-p_port);
        if(p_end == NULL) {
            
            len =str_end - (p_port + 1);
            if(len <= 0) return i;
            
            addr[i].port = ngx_atoi(p_port+1, len);
            addr[i].host .data = p_start;
            addr[i].host.len = p_port - p_start;

            return i+1;
        } 
        else{
            len = p_end -(p_port +1) ;
            if(len <= 0) return i;

            port =ngx_atoi(p_port+1, len);
            addr[i].port = port;
            addr[i].host.data = p_start;
            
            addr[i].host.len = p_port - p_start;
            p_start = p_end+1;

        }
    }

    return i;
     
}


static char *
ngx_http_huidu_redis_address(ngx_conf_t *cf, ngx_command_t *cmd, void *conf){

    ngx_http_huidu_conf_t   *		scf = conf;
    ngx_str_t  				*			value;
    ngx_uint_t  						i;
    ngx_uint_t 							redis_count;
    ngx_http_huidu_redis_address_t * addr = NULL;
	huidu_address_info 				saddr[MAX_REDIS_COUNT];
    ngx_http_huidu_redis_address_t 	redis_add[MAX_REDIS_COUNT];

    pid_t ngx_pid_u;

    ngx_pid_u = getpid();

    printf(">>>>>>>> ngx_http_huidu_redis_address pid = %d\n", ngx_pid_u);

    if(NULL == scf){
       ngx_conf_log_error(NGX_LOG_ERR, cf, 0,"ERROR !ngx_http_huidu_redis_address ngx_http_huidu_conf_t = null");
       return NGX_CONF_ERROR;
    }
    value = cf->args->elts;

    redis_count = get_redis_info(cf, &value[1], redis_add, MAX_REDIS_COUNT);
     if(redis_count  == 0 ) {
        ngx_conf_log_error(NGX_LOG_ERR, cf, 0,"no redis address");
        return NGX_CONF_ERROR;
    }

    addr = ngx_pcalloc(cf->pool, sizeof(ngx_http_huidu_redis_address_t) * redis_count);
    scf->redis_addr = addr;
    scf->redis_count  =redis_count;
    if(scf->redis_addr == NULL){
        return  NGX_CONF_ERROR;
    }
    for(i = 0; i < scf->redis_count ; i ++){
		saddr[i].port = redis_add[i].port;
		saddr[i].ip.len = redis_add[i].host.len;
		saddr[i].ip.data = redis_add[i].host.data;
        scf->redis_addr[i] = redis_add[i];
    }


	set_redis_addr(saddr, scf->redis_count);

	g_redis_addr= scf->redis_addr;
	g_redis_count=scf->redis_count;

	printf(">>>>>>>> ngx_http_huidu_redis_address end\n");
    return NGX_CONF_OK;
}


static char *
ngx_http_set_huidu_header_token(ngx_conf_t *cf, ngx_command_t *cmd, void *conf){

	ngx_http_huidu_conf_t*		scf = conf;
	ngx_str_t *		value;

	printf(">>>>>>>> ngx_http_set_huidu_header_token start\n");

	if(NULL == scf){
	   ngx_conf_log_error(NGX_LOG_ERR, cf, 0,"ERROR !ngx_http_set_huidu_header_token ngx_http_huidu_conf_t = null");
	   return NGX_CONF_ERROR;
	}

	value = cf->args->elts;

	scf->header_key  = value[1];
	scf->header_value = value[2];


//	ngx_conf_log_error(NGX_LOG_ERR, cf, 0,"ERROR !ngx_http_set_huidu_header_token  key =%V ,value = %V\n", &(scf->header_key), &(scf->header_value));
	printf(">>>>>>>> ngx_http_set_huidu_header_token end\n");
	return NGX_CONF_OK;

}


static char *
ngx_http_set_model_switch(ngx_conf_t *cf, ngx_command_t *cmd, void *conf){

	ngx_http_huidu_conf_t*		scf = conf;
	ngx_str_t *		value;
	ngx_int_t 		model_switch;

	if(NULL == scf){
	   ngx_conf_log_error(NGX_LOG_ERR, cf, 0,"ERROR !ngx_http_set_model_switch ngx_http_huidu_conf_t = null");
	   return NGX_CONF_ERROR;
	}

	value = cf->args->elts;

	model_switch = ngx_atoi(value[1].data, value[1].len);
	if(NGX_ERROR == model_switch){
		 ngx_conf_log_error(NGX_LOG_ERR, cf, 0,"ERROR !ngx_http_set_model_switch  reject_type default %d\n", DEFAULT_MODEL_SWITCH);
		 model_switch = DEFAULT_MODEL_SWITCH;
	}
	if(model_switch >0 )
		scf->model_switch = 1;
	else
		scf->model_switch = 0;
//	ngx_conf_log_error(NGX_LOG_ERR, cf, 0,"ERROR !ngx_http_set_model_switch  set ngx_http_set_model_switch  %d\n",scf->model_switch);


	return NGX_CONF_OK;
}

static ngx_int_t
ngx_http_huidu_init(ngx_conf_t *cf)
{
    ngx_http_handler_pt        *h;
    ngx_http_core_main_conf_t  *cmcf;


    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

    h = ngx_array_push(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers);
    if (h == NULL) {
        return NGX_ERROR;
    }

    *h = ngx_http_huidu_handler;


    return NGX_OK;
}
